// This is a simple implementation of Promises ,see https://promisesaplus.com/,
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
package promise

import (
	"fmt"
	"sync/atomic"
)

/*
func getid(promise){

}
func delete(promise){
}
func load(promise){
}
getid() //promise

	.then(func(result){
		delete(id)
	})//promise

.then(func(){load()}) //promise
*/
const (
	state_PENDING   int8 = 0
	state_FULFILLED int8 = 1
	state_REJECTED  int8 = 2
)

type OnFulfillFunc func(result any) (any, error)
type OnRejectFunc func(err error) (any, error)
type Resolve func(any)
type Reject func(error)

type ExecutorFunc func(resolve Resolve, reject Reject)
type Promise struct {
	value          any
	handlerChannel chan settledHandler
	state          int8
	done           *atomic.Bool
	workerStarted  *atomic.Bool
}
type settledHandler struct {
	fulfillHandler func(any)
	rejectHandler  func(error)
}

type Thenable interface {
	Then(OnFulfillFunc, OnRejectFunc) *Promise
	Catch(OnRejectFunc) *Promise
	Finally(func()) *Promise
}

// func Promise(executor *func()) {

// }

func New(executor ExecutorFunc) *Promise {
	promise := &Promise{done: &atomic.Bool{}, state: state_PENDING, workerStarted: &atomic.Bool{}}
	promise.handlerChannel = make(chan settledHandler, 4)
	promise.done.Store(false)

	promise.do(executor)
	return promise
}
func (promise *Promise) Catch(onRejectFunc OnRejectFunc) *Promise {
	return promise.Then(nil, onRejectFunc)
}
func (promise *Promise) Finally(onFinally func()) *Promise {
	return promise.Then(func(result any) (any, error) { onFinally(); return nil, nil }, nil)
}

// This method reference the Promises/A+ spec. It explains things very clearly so I recommend you read it.
func (promise *Promise) Then(onFulfillFunc OnFulfillFunc, onRejectFunc OnRejectFunc) *Promise {

	thenExecutor := func(resolve Resolve, reject Reject) {

		fulfillHandler := func(result any) {
			defer func() {
				if err := recover(); nil != err {

					reject(fmt.Errorf("%v", err))
				}
			}()
			if nil != onFulfillFunc {
				resultNew, err := onFulfillFunc(result)
				if nil != err {
					reject(err)
				}
				resolve(resultNew)
			} else {
				resolve(result)
			}

		}

		rejectHandler := func(err error) {
			defer func() {
				if err := recover(); nil != err {

					reject(fmt.Errorf("%v", err))
				}
			}()
			if nil != onRejectFunc {
				result, errNew := onRejectFunc(err)
				if nil != errNew {
					reject(errNew)
				}
				resolve(result)
			} else {
				reject(err)
			}

		}
		//根据上个promise的结果来调用具体的handler
		promise.thenHandle(fulfillHandler, rejectHandler)
	}

	return New(thenExecutor)

}
func (promise *Promise) thenHandle(fulfillHandler func(any), rejectHandler func(error)) {
	promise.handlerChannel <- settledHandler{fulfillHandler: fulfillHandler, rejectHandler: rejectHandler}

}
func (promise *Promise) handle(settledHandler settledHandler) {
	switch promise.state {
	case state_PENDING:
		return
	case state_FULFILLED:
		if nil != settledHandler.fulfillHandler {
			settledHandler.fulfillHandler(promise.value)
		}

	case state_REJECTED:
		if nil != settledHandler.rejectHandler {
			settledHandler.rejectHandler(promise.value.(error))
		}

	}

}
func (promise *Promise) fulfill(result any) {
	if promise.state == state_PENDING {
		promise.state = state_FULFILLED
		promise.value = result
		promise.startWorker()

	}

}

func (promise *Promise) reject(result error) {
	if promise.state == state_PENDING {
		promise.state = state_REJECTED
		promise.value = result
		promise.startWorker()
	}

}

func (promise *Promise) startWorker() {

	if promise.workerStarted.CompareAndSwap(false, true) {
		go func() {
			if promise.state > state_PENDING {
				for v := range promise.handlerChannel {
					promise.handle(v)
				}

			} else {
				promise.workerStarted.Store(false)
			}

		}()

	}

}

func (promise *Promise) resolve(result any) {
	if nil != result {

		then, ok := result.(Thenable)
		if ok {
			onFulfillHandler := func(result any) (any, error) {
				promise.resolve(result)
				return nil, nil
			}
			onRejectHandler := func(err error) (any, error) {
				promise.reject(err)
				return nil, nil

			}
			then.Then(onFulfillHandler, onRejectHandler)
			return
		}

	}

	if promise.done.CompareAndSwap(false, true) {
		if nil != result {
			err, ok := result.(error)
			if ok {
				promise.reject(err)
				return
			}
		}

		promise.fulfill(result)
	}

}
func (promise *Promise) do(fn ExecutorFunc) {
	defer func() {
		if err := recover(); nil != err {

			promise.reject(fmt.Errorf("%v", err))
		}
	}()
	fn(promise.resolve, promise.reject)

}
